Skip to content

OpenClaw 插件开发从入门到精通

发布日期: 2026-03-18
分类: 开发进阶
标签: OpenClaw, 插件,开发,扩展,API


为什么需要开发插件?

OpenClaw 内置功能强大,但遇到以下场景时需要自定义插件:

  1. 集成内部系统 - CRM、ERP、OA 等
  2. 特殊业务逻辑 - 公司特有的工作流程
  3. 性能优化 - 缓存、批处理等
  4. 数据导出 - 定制化报表
  5. 第三方服务 - 未在官方支持列表中的服务

插件架构

插件类型

类型用途示例
Tool 插件扩展工具自定义 API 调用
Skill 插件封装工作流自动化任务
Channel 插件消息渠道企业微信、钉钉
Model 插件AI 模型本地部署模型

插件生命周期

加载 → 初始化 → 执行 → 清理
  ↓        ↓        ↓       ↓
验证配置  建立连接  处理请求  释放资源

开发环境搭建

步骤 1:创建插件目录

bash
mkdir -p ~/.openclaw/plugins/my-custom-plugin
cd ~/.openclaw/plugins/my-custom-plugin

# 初始化项目
npm init -y
npm install openclaw-sdk

步骤 2:目录结构

my-custom-plugin/
├── package.json
├── index.js           # 插件入口
├── src/
│   ├── tools/         # 工具插件
│   │   └── custom-api.js
│   ├── skills/        # 技能插件
│   │   └── workflow.js
│   └── utils/         # 工具函数
│       └── helpers.js
├── config/
│   └── default.json   # 默认配置
└── README.md

实战 1:开发 Tool 插件

场景:集成公司内部 API

javascript
// src/tools/custom-api.js
const axios = require('axios');

class CustomAPITool {
  constructor(config) {
    this.baseUrl = config.baseUrl;
    this.apiKey = config.apiKey;
    this.timeout = config.timeout || 5000;
  }

  // 工具定义
  static get definition() {
    return {
      name: 'custom_api',
      description: '调用公司内部 API 系统',
      parameters: {
        type: 'object',
        properties: {
          endpoint: {
            type: 'string',
            description: 'API 端点路径'
          },
          method: {
            type: 'string',
            enum: ['GET', 'POST', 'PUT', 'DELETE'],
            description: 'HTTP 方法'
          },
          data: {
            type: 'object',
            description: '请求数据'
          }
        },
        required: ['endpoint', 'method']
      }
    };
  }

  // 执行方法
  async execute(params) {
    const { endpoint, method, data } = params;

    try {
      const response = await axios({
        method,
        url: `${this.baseUrl}${endpoint}`,
        headers: {
          'Authorization': `Bearer ${this.apiKey}`,
          'Content-Type': 'application/json'
        },
        data,
        timeout: this.timeout
      });

      return {
        success: true,
        data: response.data,
        status: response.status
      };
    } catch (error) {
      return {
        success: false,
        error: error.message,
        status: error.response?.status
      };
    }
  }
}

module.exports = CustomAPITool;

注册插件

javascript
// index.js
const CustomAPITool = require('./src/tools/custom-api');

module.exports = {
  name: 'my-custom-plugin',
  version: '1.0.0',
  description: '公司内部 API 集成插件',

  // 初始化
  async init(config) {
    console.log('🔌 初始化自定义插件...');
    
    // 注册工具
    this.registerTool('custom_api', new CustomAPITool(config));
    
    console.log('✅ 插件初始化完成');
  },

  // 清理
  async cleanup() {
    console.log('🧹 清理插件资源...');
  }
};

配置插件

json
{
  "plugins": {
    "entries": {
      "my-custom-plugin": {
        "path": "~/.openclaw/plugins/my-custom-plugin",
        "config": {
          "baseUrl": "https://api.company.com",
          "apiKey": "your-api-key",
          "timeout": 5000
        }
      }
    }
  }
}

使用插件

javascript
// 在技能中调用
{
  "name": "query-user-info",
  "actions": [
    {
      "tool": "custom_api",
      "params": {
        "endpoint": "/users/123",
        "method": "GET"
      }
    }
  ]
}

实战 2:开发 Skill 插件

场景:自动化审批流程

javascript
// src/skills/approval-workflow.js
class ApprovalWorkflow {
  constructor(config) {
    this.db = config.database;
    this.notifyChannel = config.notifyChannel;
  }

  static get definition() {
    return {
      name: 'approval_workflow',
      description: '自动化审批工作流',
      triggers: ['审批', 'approve', '申请'],
      parameters: {
        type: 'object',
        properties: {
          type: {
            type: 'string',
            enum: ['leave', 'expense', 'purchase'],
            description: '审批类型'
          },
          amount: {
            type: 'number',
            description: '金额(如适用)'
          },
          reason: {
            type: 'string',
            description: '申请原因'
          }
        },
        required: ['type', 'reason']
      }
    };
  }

  async execute(params) {
    const { type, amount, reason } = params;

    // 1. 创建审批单
    const approvalId = await this.createApproval({
      type,
      amount,
      reason,
      status: 'pending',
      createdAt: new Date()
    });

    // 2. 确定审批人
    const approvers = await this.determineApprovers(type, amount);

    // 3. 发送审批通知
    await this.sendNotification(approvers, {
      approvalId,
      type,
      amount,
      reason
    });

    // 4. 等待审批结果
    const result = await this.waitForApproval(approvalId);

    // 5. 执行后续操作
    if (result.approved) {
      await this.executeApproval(approvalId);
      return {
        status: 'approved',
        message: '✅ 审批通过'
      };
    } else {
      return {
        status: 'rejected',
        message: '❌ 审批拒绝:' + result.reason
      };
    }
  }

  async createApproval(data) {
    // 实现创建逻辑
    return 'APR-' + Date.now();
  }

  async determineApprovers(type, amount) {
    // 根据类型和金额确定审批人
    if (amount > 10000) {
      return ['manager', 'director', 'cfo'];
    } else if (amount > 5000) {
      return ['manager', 'director'];
    } else {
      return ['manager'];
    }
  }

  async sendNotification(approvers, data) {
    // 发送飞书/企业微信通知
    const message = `
📋 新的审批申请

类型:${data.type}
金额:${data.amount}元
原因:${data.reason}

请点击链接审批:
http://oa.company.com/approval/${data.approvalId}
    `;

    for (const approver of approvers) {
      await this.sendMessage(approver, message);
    }
  }

  async waitForApproval(approvalId) {
    // 轮询审批结果
    return new Promise((resolve) => {
      const checkInterval = setInterval(async () => {
        const status = await this.getApprovalStatus(approvalId);
        if (status !== 'pending') {
          clearInterval(checkInterval);
          resolve(status);
        }
      }, 5000);

      // 超时处理
      setTimeout(() => {
        clearInterval(checkInterval);
        resolve({ approved: false, reason: '超时未审批' });
      }, 86400000); // 24 小时超时
    });
  }
}

module.exports = ApprovalWorkflow;

实战 3:开发 Channel 插件

场景:集成企业微信

javascript
// src/channels/wechat-work.js
const axios = require('axios');

class WeChatWorkChannel {
  constructor(config) {
    this.corpId = config.corpId;
    this.agentId = config.agentId;
    this.secret = config.secret;
    this.accessToken = null;
    this.tokenExpiry = null;
  }

  static get definition() {
    return {
      name: 'wechat_work',
      description: '企业微信消息渠道'
    };
  }

  async init() {
    await this.refreshToken();
    
    // 定期刷新 Token
    setInterval(() => this.refreshToken(), 7200000); // 2 小时
  }

  async refreshToken() {
    const response = await axios.get(
      `https://qyapi.weixin.qq.com/cgi-bin/gettoken`,
      {
        params: {
          corpid: this.corpId,
          corpsecret: this.secret
        }
      }
    );

    this.accessToken = response.data.access_token;
    this.tokenExpiry = Date.now() + 7200000;
  }

  async sendMessage(userId, message) {
    await axios.post(
      `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${this.accessToken}`,
      {
        touser: userId,
        msgtype: 'text',
        agentid: this.agentId,
        text: {
          content: message
        }
      }
    );
  }

  async sendMarkdownMessage(userId, content) {
    await axios.post(
      `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${this.accessToken}`,
      {
        touser: userId,
        msgtype: 'markdown',
        agentid: this.agentId,
        markdown: {
          content
        }
      }
    );
  }

  async sendCardMessage(userId, card) {
    await axios.post(
      `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${this.accessToken}`,
      {
        touser: userId,
        msgtype: 'template_card',
        agentid: this.agentId,
        template_card: card
      }
    );
  }
}

module.exports = WeChatWorkChannel;

调试技巧

1. 本地测试

javascript
// test.js
const CustomAPITool = require('./src/tools/custom-api');

async function test() {
  const tool = new CustomAPITool({
    baseUrl: 'https://api.test.company.com',
    apiKey: 'test-key'
  });

  const result = await tool.execute({
    endpoint: '/users/123',
    method: 'GET'
  });

  console.log('测试结果:', result);
}

test();

2. 日志记录

javascript
const debug = require('debug')('my-custom-plugin');

class MyTool {
  async execute(params) {
    debug('执行参数:', params);
    
    try {
      const result = await this.doSomething(params);
      debug('执行结果:', result);
      return result;
    } catch (error) {
      debug('执行错误:', error);
      throw error;
    }
  }
}

3. 热重载

javascript
// 开发模式下支持热重载
if (process.env.NODE_ENV === 'development') {
  require('fs').watch('./src', (eventType, filename) => {
    console.log(`🔄 检测到文件变化:${filename}`);
    // 重新加载插件
    this.reload();
  });
}

发布插件

1. 打包

bash
npm pack
# 生成 my-custom-plugin-1.0.0.tgz

2. 发布到 npm

bash
npm publish

3. 安装插件

bash
# 从 npm 安装
npm install -g @your-org/openclaw-custom-plugin

# 从本地安装
npm install -g ./my-custom-plugin-1.0.0.tgz

最佳实践

✅ 应该做的

  1. 错误处理 - 捕获异常,返回友好错误信息
  2. 日志记录 - 记录关键操作,便于排查问题
  3. 配置验证 - 启动时验证配置完整性
  4. 资源清理 - 释放连接、定时器等资源
  5. 文档完善 - README、API 文档、示例代码

❌ 不应该做的

  1. 硬编码凭证 - 使用环境变量或配置文件
  2. 阻塞操作 - 使用异步,避免阻塞主线程
  3. 无限重试 - 设置重试次数上限
  4. 忽略超时 - 所有网络请求设置超时
  5. 泄露敏感信息 - 日志中脱敏

插件示例库

官方插件

  • @openclaw/feishu - 飞书集成
  • @openclaw/wechat - 微信集成
  • @openclaw/email - 邮件服务
  • @openclaw/database - 数据库连接

社区插件

  • @community/jira - Jira 集成
  • @community/github - GitHub 集成
  • @community/slack - Slack 集成
  • @community/notion - Notion 集成

总结

插件开发核心要点:

  1. 理解架构 - Tool、Skill、Channel 三种类型
  2. 规范开发 - 遵循接口定义和生命周期
  3. 完善测试 - 单元测试 + 集成测试
  4. 文档齐全 - README、示例、API 文档
  5. 持续维护 - 修复 bug、添加功能

下一步

  • 查看官方 SDK 文档
  • 参考示例插件代码
  • 加入开发者社区

相关文档:

Released under the MIT License.